// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


#ifndef CLOUDAPPENGINE_CAESOCKETAGENT_H
#define CLOUDAPPENGINE_CAESOCKETAGENT_H

#include <list>

#include "../cae_socket/CaeSocket.h"
#include "../cae_service/CaeTcpClientErrorTimer.h"
#include "../cae_dataHandler/CaeClientDataHandleThread.h"
#include "../cae_dataHandler/CaeVideoPipeDataHandleThread.h"
#include "../cae_common/CaeConfigManage.h"
#include "../cae_dataHandler/CaeAudioPipeDataHandleThread.h"
#include "../cae_service/CaeClientNotifyTool.h"
#include "../cae_dataHandler/CaeControlCmdHandleThread.h"
#include "../cae_dataHandler/CaeVirtualDeviceDataHandlerThread.h"

class CaeClientNotice : public CaeEventNotice {
public:
    CaeTcpClientErrorTimer *m_errorTimer;

    CaeClientNotice()
    {
        Init();
    }

    ~CaeClientNotice() override
    {
        this->m_errorTimer->Stop();
        CaeTimerManage::GetInstance().DeleteTimer(m_errorTimer);
        if (m_errorTimer != nullptr) {
            delete m_errorTimer;
            m_errorTimer = nullptr;
        }
    }

    void Init()
    {
        this->m_errorTimer = new CaeTcpClientErrorTimer(CaeConfigManage::GetOutTimes() *
                                                        CaeConfigManage::GetBaseTime()); // ms
        if (this->m_errorTimer == nullptr) {
            LOGE("Init error, new CaeTcpClientErrorTimer fail.");
            return;
        }
        this->m_errorTimer->Start();
        CaeTimerManage::GetInstance().AddTimer(m_errorTimer);
    }

    void Notice(void *tcpSocket) override
    {
        LOGI("CaeClientSocket: error.");
    }

    void RestartErrorTimer()
    {
        this->m_errorTimer->Restart();
    }
};

class CaeSocketAgent {
public:
    ~CaeSocketAgent();

    static CaeSocketAgent &GetInstance();
    CaeSocketAgent(const CaeSocketAgent &) = delete;
    CaeSocketAgent &operator = (const CaeSocketAgent &) = delete;

    void LockClientSocket();

    void UnlockClientSocket();

    bool IsSocketValid();

    void SetClientSocket(CaeSocket *socket);

    int RecvCmdFromRTSA(void * pkt, size_t size);

    int Send(void *pkt, size_t size);

    int Recv(void *pPkt, size_t size);

    void Reset();

    void DisableSend();

    void EnableSend();

    void RestartErrorTimer();

    void CloseOldClient();

    void CloseOldSocket();

    void UpdateErrorTimerTime();

    void UpdateNoTouchTimer();

    bool StartNotouchTimer();

    void StopNotouchTimer();

    void StartPauseTimer();

    void StopPauseTimer();

    bool StartTrailTimer();

    void StopTrailTimer();

    void PauseClient();

    void SendVirtualDeviceStartRequest();

    volatile bool m_sendFlag;

protected:
    CaeSocketAgent();

private:
    CaeSocket *m_clientSocket;
    void *m_notice;
    CaeTcpClientPauseTimer *m_pauseTimer;
    CaeTrialTimeoutTimer *m_trailTimer;
    CaeNotouchTimer *m_notouchTimer;
    pthread_mutex_t m_lock;
    pthread_mutex_t m_clientSocketLock;
    CaeClientDataHandleThread *m_clientDataHandleThread;
    CaeVideoPipeDataHandleThread *m_videoPipeDataHandleThread;
    CaeAudioPipeDataHandleThread *m_audioPipeDataHandleThread;
    CaeControlCmdHandleThread *m_cmdPipeDataHandleThread;
    CaeVirtualDeviceDataHandlerThread *m_virtualDeviceDataHandleThread;

    void DestroyThreads() noexcept;
    void CreateClientNotice();
    void CloseVirtualDevice();
};

#endif // CLOUDAPPENGINE_CAESOCKETAGENT_H
